package com.ponhu.ea2.goodsCenter.provider.facade.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ponhu.ea2.common.basic.model.PagedResult;
import com.ponhu.ea2.common.basic.model.ResponseBean;
import com.ponhu.ea2.constants.CommonConstant;
import com.ponhu.ea2.constants.DictConstant;
import com.ponhu.ea2.dto.*;
import com.ponhu.ea2.entity.*;
import com.ponhu.ea2.enums.*;
import com.ponhu.ea2.goodsCenter.provider.biz.OperationRecordLogsCoreService;
import com.ponhu.ea2.goodsCenter.provider.biz.PonHuGoodsService;
import com.ponhu.ea2.goodsCenter.provider.mapper.*;
import com.ponhu.ea2.goodsCenter.provider.service.*;
import com.ponhu.ea2.goodsCenter.provider.support.RedisSupportService;
import com.ponhu.ea2.goodsCenter.provider.utils.*;
import com.ponhu.ea2.query.InventoryDownloadTaskQuery;
import com.ponhu.ea2.query.InventoryInfoQuery;
import com.ponhu.ea2.query.LiveGoodsInfoQuery;
import com.ponhu.ea2.service.facade.CommonApiService;
import com.ponhu.ea2.service.facade.GoodsCenterForThirdPartyService;
import com.ponhu.ea2.service.facade.InventoryApiService;
import com.ponhu.ea2.stockcenter.service.bms.StockCommonService;
import com.ponhu.ea2.stockcenter.vo.InventoryStockWhVO;
import com.ponhu.ea2.stockcenter.vo.ReturnResponseBean;
import com.ponhu.ea2.stockcenter.vo.StockMqVO;
import com.ponhu.ea2.stockcenter.vo.StockWarehouseVO;
import com.ponhu.ea2.support.exception.BusinessRuntimeException;
import com.ponhu.ea2.utils.BarCodeUtils;
import com.ponhu.ea2.utils.QiNiuUploadUtils;
import com.ponhu.ea2.vo.*;
import io.netty.util.concurrent.FastThreadLocal;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.apache.dubbo.config.annotation.Method;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.ponhu.ea2.enums.ResponseCodeEnum.*;

@Slf4j
@RefreshScope
@DubboService(version = "1.0.0", interfaceClass = InventoryApiService.class, timeout = 6000,
        methods = {
                @Method(name = "aiPictureInventoryBatchSync", timeout = 1800000),
                @Method(name = "queryPhGoodsInfoForLiveVo", timeout = 60000),
                @Method(name = "batchEditPriceUpload", timeout = 600000),
                @Method(name = "createDownloadTask", timeout = 60000),
                @Method(name = "getAllInventorys", timeout = 1800000),
                // 批量更新备注超时时间设定为3分钟
                @Method(name = "batchEditRemarksUpload", timeout = 180000)
        })
public class InventoryApiServiceImpl implements InventoryApiService {

    @Value("${goods.config.biz.inventoryDownloadTools.tempDir}")
    private String tempDir;
    @Value("${goods.config.biz.inventoryDownloadTools.tempZip}")
    private String tempZip;

    @Autowired
    private FastThreadLocal<String> traceIdLocal;
    @Autowired
    private FastThreadLocal<Boolean> subQueryEmptyLocal;
    @Autowired
    private FastThreadLocal<List<Material>> materialsLocal;
    @Autowired
    private FastThreadLocal<List<PhCategoryLevel3Vo>> categorysLocal;

    @Resource
    BaoxjUtils baoxjUtils;

    @DubboReference(version = "1.0.0", check = false)
    private StockCommonService stockCommonService;

    @Autowired
    private GoodsMapper goodsMapper;
    @Autowired
    private MaterialMapper materialMapper;
    @Autowired
    private InventoryMapper inventoryMapper;
    @Autowired
    private PhCategoryMapper phCategoryMapper;
    @Autowired
    private PhInventoryDetailMapper phInventoryDetailMapper;
    @Autowired
    private GoodsInventoryMdMapper goodsInventoryMdMapper;
    @Autowired
    private PhInventoryAccessoryMapper phInventoryAccessoryMapper;

    @Autowired
    private GoodsService goodsService;
    @Autowired
    private PhSeriesService phSeriesService;
    @Autowired
    private MaterialService materialService;
    @Autowired
    private PhBrandService phBrandService;
    @Autowired
    private PhModelService phModelService;
    @Autowired
    private InventoryService inventoryService;
    @Autowired
    private PhPropertyService phPropertyService;
    @Autowired
    private CommonApiService commonApiService;
    @Autowired
    private PonHuGoodsService ponHuGoodsService;
    @Autowired
    private RedisSupportService redisSupportService;
    @Autowired
    private CloudTradeGoodsService cloudTradeGoodsService;
    @Autowired
    private GoodsInventoryMdService goodsInventoryMdService;
    @Autowired
    private PhInventoryAppraisalService phInventoryAppraisalService;
    @Autowired
    private PhInventoryAccessoryService phInventoryAccessoryService;
    @Autowired
    private PhInventoryCodeGenerateService phInventoryCodeGenerateService;
    @Autowired
    private OperationRecordLogsCoreService operationRecordLogsCoreService;
    @Autowired
    private GoodsCenterForThirdPartyService goodsCenterForThirdPartyService;
    @Autowired
    private PhInventoryDownloadLogsService phInventoryDownloadLogsService;
    @Autowired
    private PhMaterialPropertyValueMdService phMaterialPropertyValueMdService;
    @Autowired
    private PhWarehousingMethodCodeService phWarehousingMethodCodeService;
    @Autowired
    private PhInventoryPropertyValueMdService phInventoryPropertyValueMdService;
    @Autowired
    private PhInventoryBatchEditPriceLogsService phInventoryBatchEditPriceLogsService;
    @Resource
    private PhinventoryToAmqpService phinventoryToAmqpService;
    @Resource
    DingTalkHandler dingTalkHandler;

    /**
     * 根据入库方式生成货品编码
     *
     * @return
     */
    @Override
    public List<String> getInventoryCodeByType(String type, Integer size, ThirdSystemEnum systemEnum, Integer isSecondhand) {

        PhWarehousingMethodCode methodCode = phWarehousingMethodCodeService.getMethodCodeByType(type);
        if (ObjectUtils.isEmpty(methodCode))
            throw new BusinessRuntimeException(INVENTORY_CODE_GENERATE_EXCEPTION_800212);

        String redisKey = RedisUtils.getRedisKey(CommonConstant.GENERATE_INVENTORY_CODE_LOCK, methodCode.getCode());
        return redisSupportService.wrapWithWriteLock(redisKey, () ->
                generateInventoryCodes(type, methodCode.getCode(), size, systemEnum, isSecondhand));
    }

    @Override
    public List<String> getInventoryCodeByType(String type, Integer size, ThirdSystemEnum systemEnum) {
        return getInventoryCodeByType(type, size, systemEnum, null);
    }

    /**
     * 判断货品编码是否已经使用并入库：true-已使用，false-未使用
     *
     * @param inventoryCode
     * @return
     */
    @Override
    public Boolean checkInventoryCodeIsUsed(String inventoryCode) {
        Inventory inventory = inventoryService.getInventoryByCode(inventoryCode);
        return ObjectUtils.isNotEmpty(inventory);
    }

    /**
     * 更新商品和货品信息
     *
     * @param inventory
     * @param goods
     * @return
     */
    @Override
    public boolean updateInventoryAndGoods(Inventory inventory, Goods goods) {
        Inventory oldInventory = inventoryService.getInventoryByCode(inventory.getInventoryCode());
        if (oldInventory == null) {
            throw new BusinessRuntimeException(INVENTORY_EDIT_EXCEPTION_800209);
        }
        inventory.setId(oldInventory.getId());
        BeanUtils.copyProperties(inventory, oldInventory);

        if (!inventoryService.updateById(oldInventory)) {
            //更新货品失败
            throw new BusinessRuntimeException(INVENTORY_EDIT_EXCEPTION_800204);

        }
        GoodsInventoryMd goodsInventoryMd = goodsInventoryMdService.getGoodsInventoryMdByInventoryId(oldInventory.getId());
        if (goodsInventoryMd == null) {
            throw new BusinessRuntimeException(GOODS_DATA_NOT_EXISTS_800602);
        }

        Goods oldGoods = goodsService.getById(goodsInventoryMd.getGoodsId());
        goods.setId(goodsInventoryMd.getGoodsId());
        BeanUtils.copyProperties(goods, oldGoods);

        if (!goodsService.updateById(oldGoods)) {
            //更新商品失败
            throw new BusinessRuntimeException(GOODS_EDIT_EXCEPTION_800601);
        }
        return true;
    }

    /**
     * 条件查询全部货品列表
     *
     * @return
     */
    @Override
    public PagedResult<PhInventoryListVo> getAllInventorysByQuery(InventoryInfoQuery inventoryInfoQuery) {

        // 条件查询分页货品实体信息列表
        IPage<Inventory> iPage = inventoryService.getAllInventorys(inventoryInfoQuery);

        // 货品实体信息转化为响应数据模型
        List<Inventory> inventoryList = iPage.getRecords();
        List<PhInventoryListVo> inventoryListVos = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(inventoryList)) {
            inventoryListVos = dealWithQueryVo(inventoryList);
        }
        subQueryEmptyLocal.remove();
        materialsLocal.remove();
        categorysLocal.remove();

        // 构建响应数据分页模型
        return new PagedResult<>(inventoryListVos, (int) iPage.getCurrent(), iPage.getTotal(), (int) iPage.getSize());
    }

    /**
     * 根据货品编码获取中检溯源码信息
     *
     * @param inventoryCode
     * @return
     */
    @Override
    public PhInventoryAppraisalVo getSourceCodeByCode(String inventoryCode) {
        PhInventoryAppraisal appraisal = phInventoryAppraisalService.getSourceCodeByCode(inventoryCode);
        return ProBeanUtils.copyProperties(appraisal, PhInventoryAppraisalVo.class);
    }

    /**
     * 同步货品中检结果
     *
     * @param phInventoryAppraisalDto
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean syncInventoryAppraisal(PhInventoryAppraisalDto phInventoryAppraisalDto) {

        CompletableFuture<PhInventoryAppraisalDto> oldFuture = CompletableFuture
                .supplyAsync(() -> {
                    Inventory inventory = inventoryService.getInventoryByCode(phInventoryAppraisalDto.getInventoryCode());
                    PhInventoryAppraisal appraisal = phInventoryAppraisalService
                            .getSourceCodeByCode(phInventoryAppraisalDto.getInventoryCode());
                    PhInventoryAppraisalDto oldDto = new PhInventoryAppraisalDto();
                    if (ObjectUtils.isNotEmpty(appraisal)) BeanUtils.copyProperties(appraisal, oldDto);
                    return oldDto.setInventoryId(inventory.getId())
                            .setIsDoneCcic(inventory.getIsDoneCcic()).initIsDoneCcicStr();
                });

        // 根据货品编码修改货品中检状态
        inventoryService.syncInventoryAppraisal(phInventoryAppraisalDto);

        // 保存货品中检结果
        phInventoryAppraisalService.saveInventoryAppraisalByDto(phInventoryAppraisalDto);

        phInventoryAppraisalDto.setInventoryId(oldFuture.join().getInventoryId()).initIsDoneCcicStr();
        OperatorDto operatorDto = new OperatorDto()
                .setOperatorId(0L).setOperatorName("Erp-中检调用");
        operationRecordLogsCoreService.operationRecordLogs(oldFuture,
                phInventoryAppraisalDto, "同步货品中检结果记录操作日志异常", operatorDto);

        return true;
    }

    /**
     * 根据货品ID查询货品详情
     *
     * @param inventoryId
     * @return
     */
    @Override
    public PhInventoryDetailVo getInventoryDetailById(Long inventoryId, Integer type) {

        // 根据货品ID查询货品详情VO
        PhInventoryDetailVo phInventoryDetailVo = inventoryMapper.getInventoryDetailVoById(inventoryId);
        if (ObjectUtils.isEmpty(phInventoryDetailVo)) {
            throw new BusinessRuntimeException(ResponseCodeEnum
                    .INVENTORY_EDIT_EXCEPTION_800209);
        }

        if (StringUtils.isNotEmpty(phInventoryDetailVo.getMaterialId())) {
            // 根据物料编码获取物料信息
            PhMaterialVo phMaterialVo = materialMapper.getMaterialVoById(phInventoryDetailVo.getMaterialId());
            // 根据物料ID查询物料属性并转Map
            List<PhPropertyVo> propertyValues = phMaterialPropertyValueMdService
                    .getPropertyValueMap(phMaterialVo.getMaterialId());
            phMaterialVo.setMaterialPropertyValueList(propertyValues);
            phInventoryDetailVo.setPhMaterialVo(phMaterialVo);
        }

        if (CommonConstant.INVENTORY_DETAIL_FOR_EDIT.equals(type)) {
            List<PhPropertyEditVo> propertyValueVos = phInventoryPropertyValueMdService
                    .getPropertyValueVos(inventoryId, phInventoryDetailVo.getCategoryId());
            phInventoryDetailVo.setPhPropertyVos(propertyValueVos);
        } else {
            List<PhPropertyVo> phPropertList = phInventoryPropertyValueMdService.getPropertyValueList(inventoryId);
            phInventoryDetailVo.setPhPropertList(phPropertList);
        }

        PhInevntoryGoodsDetailVo goodsVo = goodsMapper.getGoodsForInventoryVoById(inventoryId);

        Map<String, List<PhInventoryAccessoryVo>> accessoryMap = phInventoryAccessoryMapper
                .getInventoryAccessoryByIid(inventoryId).stream()
                .filter(avo -> CommonConstant.INVENTORY_DETAIL_FOR_EDIT.equals(type) || avo.getChecked())
                .collect(Collectors.groupingBy(PhInventoryAccessoryVo::getDictType));

        Map<String, String> channelsMap = commonApiService.getAllInventoryChannels().stream().collect(Collectors
                .toMap(PhSysDictItemVo::getValue, PhSysDictItemVo::getLabel));

        return phInventoryDetailVo
                .setGoodsInfoForInevntoryVo(goodsVo)
                .setAnnexList(accessoryMap.get(DictConstant.INVENTORY_ANNEX))
                .setAccessoryList(accessoryMap.get(DictConstant.INVENTORY_ACCESSORY))
                .setIvtChannelLabel(channelsMap.get(phInventoryDetailVo.getInventoryChannel().toString()));
    }

    /**
     * 根据货品编码查询货品信息,回显
     *
     * @param inventoryCode
     * @return
     */
    @Override
    public PhInventoryDetailVo getInventoryDetailByCode(String inventoryCode) {
        Inventory inventoryByCode = inventoryService.getInventoryByCode(inventoryCode);
        if (ObjectUtils.isEmpty(inventoryByCode)) {
            throw new BusinessRuntimeException(ResponseCodeEnum
                    .INVENTORY_EDIT_EXCEPTION_800209);
        }
        if (inventoryByCode.getInventoryChannel()
                .equals(InventoryChannelEnum.CLOUD_TRADE_JOINT_CONSIGNMENT.getCode())) {
            throw new BusinessRuntimeException(ResponseCodeEnum
                    .INVENTORY_EDIT_EXCEPTION_800212);
        }
        return getInventoryDetailById(inventoryByCode.getId(), 2);
    }

    /**
     * 查询货品库存状态
     *
     * @param inventoryId
     * @return
     */
    @Override
    public Integer getInventoryStockByCode(Long inventoryId) {
        List<PickInventoryStockVo> inventoryStockInfo = inventoryMapper.getInventoryStockInfo(Arrays.asList(inventoryId));
        AtomicInteger sum = new AtomicInteger();
        inventoryStockInfo.stream().forEach(item -> {
            sum.addAndGet(item.getTotalRealStockNum() == null ? 0 : item.getTotalRealStockNum());
            sum.addAndGet(item.getFreeStockNum() == null ? 0 : item.getFreeStockNum());
        });

        return sum.intValue();
    }

    /**
     * 设置货品关联物料
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean setInventoryMaterial(InventoryMaterialSetDto setDto) {

        CompletableFuture<InventoryMaterialSetDto> oldFuture = CompletableFuture
                .supplyAsync(() -> {
                    Inventory oldInventory = inventoryService.getById(setDto.getInventoryId());
                    Long oldMaterialId = oldInventory.getMaterialId();
                    String oldMaterialCode = null;
                    if (ObjectUtils.isNotEmpty(oldMaterialId)) {
                        oldMaterialCode = materialService.getById(oldMaterialId).getMaterialCode();
                    }
                    return new InventoryMaterialSetDto().setInventoryId(setDto.getInventoryId())
                            .setMaterialId(oldMaterialId).setMaterialCode(oldMaterialCode);
                }); // goodsCenterPool

        Material newMaterial = materialService.getById(setDto.getMaterialId());
        if (ObjectUtils.isEmpty(newMaterial)) {
            throw new BusinessRuntimeException(ResponseCodeEnum
                    .INVENTORY_EDIT_EXCEPTION_800211);
        }

        Inventory inventory = new Inventory()
                .setId(setDto.getInventoryId())
                .setMaterialId(setDto.getMaterialId())
                .setBrandId(newMaterial.getBrandId())
                .setCategoryId(newMaterial.getCategoryId());
        if (!inventoryService.updateById(inventory)) {
            throw new BusinessRuntimeException(ResponseCodeEnum
                    .INVENTORY_EDIT_EXCEPTION_800210);
        }

        // 获取修改前的原货品基本信息与修改的数据信息对比并记录日志
        operationRecordLogsCoreService.operationRecordLogs(oldFuture,
                setDto, "设置货品关联物料记录操作日志异常", setDto);

        return true;
    }

    /**
     * 编辑货品相关信息(附件、配件、属性)
     *
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean editPhInventory(PhInventoryDetailEditDto editDto) {

        // 获取货品基本信息原数据
        Inventory inventory = inventoryService.getById(editDto.getInventoryId());

        return editPhInventoryWithRecord(editDto, inventory, (ed, inv) -> {

            // 编辑货品基本信息
            inventoryService.editPhInventory(inventory, editDto);

            // 批量编辑货品关联附件和配件信息
            phInventoryAccessoryService.editInventoryAccessories(inventory,
                    editDto.getAnnexList(), editDto.getAccessoryList());

            // 批量编辑货品相关属性
            phInventoryPropertyValueMdService
                    .editInventoryProps(inventory.getId(), editDto.getPorpList());

            return true;
        });
    }

    public Boolean editPhInventoryWithRecord(PhInventoryDetailEditDto editDto, Inventory inventory,
                                             BiFunction<PhInventoryDetailEditDto, Inventory, Boolean> biFunction) {

        CompletableFuture<PhInventoryDetailEditDto> oldFuture = CompletableFuture
                .supplyAsync(() -> {

                    Map<String, String> accessoryMap = phInventoryAccessoryService
                            .getInventoryAccessorys(inventory.getId()).stream()
                            .collect(Collectors.groupingBy(PhInventoryAccessory::getDictType, Collectors
                                    .mapping(PhInventoryAccessory::getDictItemLabel, Collectors.joining("，"))));

                    String porps = null;
                    if (inventory.getIsSecondhand() == 1) {
                        porps = phInventoryPropertyValueMdService.getPropertyValueList(inventory.getId())
                                .stream().map(prop -> String.format("[%s：%s]", prop.getPropertyName(), prop.getPropertyValue()))
                                .collect(Collectors.joining("，"));
                    }

                    return ProBeanUtils.copyProperties(inventory, PhInventoryDetailEditDto.class)
                            .setInventoryId(inventory.getId()).setPorps(porps)
                            .setAnnexs(accessoryMap.get(DictConstant.INVENTORY_ANNEX))
                            .setAccessories(accessoryMap.get(DictConstant.INVENTORY_ACCESSORY));
                });

        Boolean apply = biFunction.apply(editDto, inventory);

        if (CollectionUtils.isNotEmpty(editDto.getPorpList())) {
            Map<String, List<PhPropertyValueDto>> propMap = editDto.getPorpList()
                    .stream().collect(Collectors.groupingBy(PhPropertyValueDto::getPropertyName));
            String porps = propMap.entrySet().stream().map(entry -> {
                String values = entry.getValue().stream()
                        .map(PhPropertyValueDto::getPropertyValue)
                        .collect(Collectors.joining(","));
                return String.format("[%s：%s]", entry.getKey(), values);
            }).collect(Collectors.joining("，"));
            editDto.setPorps(porps);
        }

        String accessories = editDto.getAccessoryList().stream().collect(Collectors
                .mapping(PhInventoryAccessoryDto::getDictItemLabel, Collectors.joining("，")));

        String annexs = editDto.getAnnexList().stream().collect(Collectors
                .mapping(PhInventoryAccessoryDto::getDictItemLabel, Collectors.joining("，")));

        editDto.setAccessories(accessories).setAnnexs(annexs);

        operationRecordLogsCoreService.operationRecordLogs(oldFuture, editDto,
                "编辑货品相关信息记录操作日志异常", editDto);

        return apply;
    }

    /**
     * 编辑货品相关商品信息
     *
     * @return
     */
    @Override
    public Boolean editInventoryGoods(PhInventoryGoodsEditDto editDto) {

        CompletableFuture<String> audioFuture = CompletableFuture.supplyAsync(() -> {
            try {
                String audioUrl = ponHuGoodsService.processSellingPointAudioUrl(editDto.getSellingPoint());
                Goods goods = new Goods()
                        .setId(editDto.getGoodsId())
                        .setSellingPointAudio(audioUrl);
                if (!goodsService.updateById(goods))
                    log.error("编辑货品信息商品卖点文本转语音保存数据库失败 ===>> goodsId={}", editDto.getGoodsId());
                return audioUrl;
            } catch (Exception e) {
                log.error("编辑货品信息商品卖点文本转语音异常 ===>> error={}", e.getMessage());
                return null;
            }
        });

        CompletableFuture<PhInventoryGoodsEditDto> oldFuture = CompletableFuture
                .supplyAsync(() -> {
                    PhInventoryGoodsEditDto oldEditDto = ProBeanUtils.copyProperties(editDto, PhInventoryGoodsEditDto.class);
                    Goods oldGoods = goodsService.getById(editDto.getGoodsId());
                    BeanUtils.copyProperties(oldGoods, oldEditDto);
                    Inventory oldInventory = inventoryService.getById(editDto.getInventoryId());
                    BeanUtils.copyProperties(oldInventory, oldEditDto);
                    return oldEditDto;
                });

        ((InventoryApiService) AopContext.currentProxy()).editInventoryGoodsTransactional(editDto);

        operationRecordLogsCoreService.operationRecordLogs(oldFuture,
                editDto, "编辑货品相关商品信息记录操作日志异常", editDto);

        // 编辑货品信息后同步胖总管
        CompletableFuture.runAsync(() -> syncThirdParties(editDto.getInventoryId(), editDto.getOperatorName(), audioFuture));

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editInventoryGoodsTransactional(PhInventoryGoodsEditDto editDto) {

        // 根据货品ID修改货品入库价
        inventoryService.editInventoryWarehousePrice(editDto);

        // 保存货品第一次编辑人的信息
        inventoryService.checkAndSaveFirstEdit(editDto);

        // 编辑货品关联商品基本信息
        goodsService.editInventoryGoodsPrices(editDto);

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean saveInventoryAndGoods(Inventory inventory, Goods goods) {
        Boolean bool = false;
        GoodsInventoryMd goodsInventoryMd = new GoodsInventoryMd();
        try {
            // 更新货品表
            int insertInventory = inventoryMapper.insert(inventory);
            if (insertInventory != 1) {
                throw new Exception("商品中心货品创建失败,货品信息为:" + inventory);
            }
            // 更新商品表
            int insertGoods = goodsMapper.insert(goods);
            if (insertGoods != 1) {
                throw new Exception("商品中心商品创建失败,商品信息为" + goods);
            }
            // 更新商品和货品表
            goodsInventoryMd.setGoodsId(goods.getId());
            goodsInventoryMd.setInventoryId(inventory.getId());
            int insertgoodsInventoryMd = goodsInventoryMdMapper.insert(goodsInventoryMd);
            if (insertgoodsInventoryMd != 1) {
                throw new Exception("商品中心商品货品中间表创建失败,商品信息为" + goods);
            }
            bool = true;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (!bool) {
                // 如果创建失败,则直接返回,同时记录日志
                log.error("商品中心创建失败,商品为{},货品为{},中间表为{}", goods.toString(), inventory.toString(), goodsInventoryMd.toString());
                return bool;
            }
        }
        return bool;
    }

    /**
     * 将数据库查询实体集合转为查询返回数据模型集合
     *
     * @param inventoryList
     * @return
     */
    public List<PhInventoryListVo> dealWithQueryVo(List<Inventory> inventoryList) {

        CompletableFuture<Map<Long, PhMaterialVo>> materialsFuture = CompletableFuture.supplyAsync(() -> {
            List<Long> materialIds = inventoryList.stream().map(Inventory::getMaterialId).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(materialIds)) return Maps.newHashMap();
            List<Material> materialList = materialService.listByIds(materialIds);

            List<Long> seriesIds = materialList.stream().map(Material::getSeriesId).collect(Collectors.toList());
            Map<Long, PhSeries> seriesMap;
            if (CollectionUtils.isEmpty(seriesIds)) seriesMap = Maps.newHashMap();
            else seriesMap = phSeriesService.listByIds(seriesIds).stream()
                    .collect(Collectors.toMap(PhSeries::getId, Function.identity()));

            List<Long> modelIds = materialList.stream().map(Material::getModelId).collect(Collectors.toList());
            Map<Long, String> modelMap;
            if (CollectionUtils.isEmpty(modelIds)) modelMap = Maps.newHashMap();
            else modelMap = phModelService.listByIds(modelIds).stream()
                    .collect(Collectors.toMap(PhModel::getId, PhModel::getModel));

            return materialList.stream().map(material -> {
                PhMaterialVo phMaterialVo = new PhMaterialVo()
                        .setMaterialId(material.getId())
                        .setMaterialCode(material.getMaterialCode())
                        .setModelName(modelMap.get(material.getModelId()));

                PhSeries phSeries = seriesMap.get(material.getSeriesId());
                if (ObjectUtils.isNotEmpty(phSeries))
                    phMaterialVo.setSeriesName(phSeries.getName())
                            .setSeriesOtherName(phSeries.getAnotherName());
                return phMaterialVo;
            }).collect(Collectors.toMap(PhMaterialVo::getMaterialId, Function.identity()));
        });

        List<PhCategoryLevel3Vo> categoryList = categorysLocal.get();
        CompletableFuture<Map<Long, String>> categorysFuture = CompletableFuture.supplyAsync(() -> {
            if (CollectionUtils.isEmpty(categoryList)) {
                List<Long> categoryIdList = inventoryList.stream().map(Inventory::getCategoryId).collect(Collectors.toList());
                categoryList.addAll(phCategoryMapper.getCategoryLevel3VosByIds(categoryIdList));
            }
            return categoryList.stream().collect(Collectors.toMap(PhCategoryLevel3Vo::getLevel3Id, category ->
                    String.format("%s-%s-%s", category.getLevel1name(), category.getLevel2name(), category.getLevel3name())));
        });

        CompletableFuture<Map<Long, String>> brandsFuture = CompletableFuture.supplyAsync(() -> {
            List<Long> brandIdList = inventoryList.stream().map(Inventory::getBrandId).distinct().collect(Collectors.toList());
            return phBrandService.listByIds(brandIdList).stream().collect(Collectors.toMap(PhBrand::getId,
                    brand -> String.format("%s %s", brand.getEnName(), brand.getCnName())));
        });

        CompletableFuture<Map<String, String>> channelsFuture = CompletableFuture.supplyAsync(() ->
                commonApiService.getAllInventoryChannels().stream().collect(Collectors
                        .toMap(PhSysDictItemVo::getValue, PhSysDictItemVo::getLabel)));

        return CompletableFuture.allOf(materialsFuture, categorysFuture, brandsFuture, channelsFuture).thenApply(x -> {
            Map<Long, PhMaterialVo> mVoMap = materialsFuture.join();
            return inventoryList.stream().map(inventory -> {
                PhInventoryListVo phInventoryListVo = ProBeanUtils
                        .copyProperties(inventory, PhInventoryListVo.class)
                        .setIdStr(inventory.getId().toString())
                        .setBrandName(brandsFuture.join().get(inventory.getBrandId()))
                        .setCategoryName(categorysFuture.join().get(inventory.getCategoryId()))
                        .setIvtChannelLabel(channelsFuture.join().get(String.valueOf(inventory.getInventoryChannel())));
                PhMaterialVo phMaterialVo = mVoMap.get(inventory.getMaterialId());
                if (ObjectUtils.isNotEmpty(phMaterialVo))
                    phInventoryListVo.setMaterialCode(phMaterialVo.getMaterialCode())
                            .setModelName(phMaterialVo.getModelName())
                            .setSeriesName(phMaterialVo.getSeriesName())
                            .setSeriesOtherName(phMaterialVo.getSeriesOtherName());
                return phInventoryListVo;
            }).collect(Collectors.toList());
        }).join();
    }

    /**
     * 编辑货品信息后同步胖总管
     *
     * @param inventoryId
     */
    @Override
    public boolean syncThirdParties(Long inventoryId, String editName) {

        return syncThirdParties(inventoryId, editName, null);
    }

    /**
     * 编辑货品信息后同步胖总管
     *
     * @param inventoryId
     */
    @Override
    public boolean syncThirdParties(Long inventoryId, String editName, CompletableFuture<String> audioFuture) {

        try {
            if (ObjectUtils.isNotEmpty(audioFuture)) {
                // 等待商品卖点文本转语音完成
                audioFuture.join();
            }

            PhInventoryBaseDto inventoryBaseDto = inventoryMapper.getInventoryVoForPonhuById(inventoryId);
            if (ObjectUtils.isEmpty(inventoryBaseDto))
                throw new RuntimeException("参数错误：货品ID查询无结果 ===>> inventoryId=" + inventoryId);
            log.info("编辑货品信息后同步胖总管 ===>> 货品ID查询结果 ===>> PhInventoryBaseDto={}",
                    JSON.toJSONString(inventoryBaseDto));

            String annexs = phInventoryAccessoryService.getInventoryAccessorys(inventoryId).stream()
                    .map(PhInventoryAccessory::getDictItemLabel).collect(Collectors.joining(","));
            inventoryBaseDto.setAnnexs(annexs);

            List<PhPropertyVo> propertyVoList = Lists.newArrayList();
            List<PhPropertyVo> ivtPropVos = phInventoryPropertyValueMdService.getPropertyValueList(inventoryId);
            propertyVoList.addAll(ivtPropVos);

            if (ObjectUtils.isNotEmpty(inventoryBaseDto.getMaterialId())) {
                List<PhPropertyVo> materialPropVos = phMaterialPropertyValueMdService
                        .getPropertyValueMap(Long.parseLong(inventoryBaseDto.getMaterialId()));
                propertyVoList.addAll(materialPropVos);
            }

            List<Long> propIds = propertyVoList.stream().map(PhPropertyVo::getPropertyId).collect(Collectors.toList());
            // 调整逻辑
            if (CollectionUtils.isNotEmpty(propIds)) {
                Map<Long, String> phNameMap = phPropertyService.listByIds(propIds)
                        .stream().filter(prop -> StringUtils.isNotBlank(prop.getPonhuName()))
                        .collect(Collectors.toMap(PhProperty::getId, PhProperty::getPonhuName));
                Map<String, String> propertyMap = propertyVoList.stream()
                        .filter(pr -> StringUtils.isNotBlank(phNameMap.get(pr.getPropertyId())))
                        .collect(Collectors.toMap(prop ->
                                        phNameMap.getOrDefault(prop.getPropertyId(), prop.getPropertyName()),
                                PhPropertyVo::getPropertyValue));
                inventoryBaseDto.setPropertyMap(propertyMap);
            }

            if (inventoryBaseDto.getInventoryChannel() == InventoryChannelEnum.CLOUD_TRADE_JOINT_CONSIGNMENT.getCode()) {
                BigDecimal supplyTaxPrice = ((PhInventoryForGoodsCenterDto) inventoryBaseDto).getSupplyTaxPrice();
                BigDecimal saleTaxPrice = ((PhInventoryForGoodsCenterDto) inventoryBaseDto).getSaleTaxPrice();
                inventoryBaseDto = ProBeanUtils.copyProperties(inventoryBaseDto, PhInventoryForCloudTradeDto.class);
                Inventory inventory = inventoryService.getById(inventoryId);
                if (ObjectUtils.isNotEmpty(inventory))
                    ((PhInventoryForCloudTradeDto) inventoryBaseDto).setInventory(inventory)
                            .setCostPrice(inventory.getInventoryCost())
                            .setWarehousingPrice(inventory.getWarehousePrice())
                            .setSupplyPrice(supplyTaxPrice)
                            .setSalesPriceGuidance(saleTaxPrice)
                            .setIsUpdate(CommonConstant.YES_OR_NO.YES);
                CloudTradeGoods goods = cloudTradeGoodsService.getCloudTradeGoodsByChannelGoodsCode(inventory.getInventoryCode());
                if (ObjectUtils.isNotEmpty(goods))
                    ((PhInventoryForCloudTradeDto) inventoryBaseDto).setShopId(goods.getAccountId().toString());
            }
            inventoryBaseDto.setOperatorName(editName);
            log.info("编辑货品信息后同步胖总管 ===>> 入参 = {}", JSON.toJSONString(inventoryBaseDto));
            ResponseBean responseBean = goodsCenterForThirdPartyService
                    .createGoodsAndInventoryForThirdpaerty(inventoryBaseDto);

            log.info("编辑货品信息后同步胖总管 ===>> Code={}", responseBean.getCode());
            return responseBean.getCode() == 200;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("编辑货品信息后同步胖总管异常 ===>> Error={} ======> 货品ID为:{}", e, inventoryId);
            return false;
        }
    }

    /**
     * 直播查询商品信息
     *
     * @param liveGoodsInfoQuery
     * @return
     */
    @Override
    public PagedResult<PhGoodsInfoForLiveVo> queryPhGoodsInfoForLiveVo(LiveGoodsInfoQuery liveGoodsInfoQuery) {
        log.info("查询直播商品数据开始,入参为：{}，时间为：{}", JSONObject.toJSONString(liveGoodsInfoQuery), LocalDateTime.now());
        Page<PhGoodsInfoForLiveVo> page = new Page(liveGoodsInfoQuery.getPageIndex(), liveGoodsInfoQuery.getPageSize());
        page.setCountId("getGoodsInfoForLiveCount");
        IPage<PhGoodsInfoForLiveVo> phGoodsInfoForLiveVoIPage =
                (IPage) inventoryMapper.getGoodsInfoForLive(page, liveGoodsInfoQuery);
        log.info("查询直播商品数据入参为：{},查询数据库时间{}", JSONObject.toJSONString(liveGoodsInfoQuery), LocalDateTime.now());
        // 处理数据
        List<PhGoodsInfoForLiveVo> list = processData(phGoodsInfoForLiveVoIPage.getRecords());
        if (CollectionUtils.isNotEmpty(list)) {
            if (StringUtils.isNotBlank(liveGoodsInfoQuery.getCreateStartTime())) {
                list.stream().sorted(Comparator.comparing(PhGoodsInfoForLiveVo::getInventoryCreateTime));
            } else if (StringUtils.isNotBlank(liveGoodsInfoQuery.getUpdateStartTime())) {
                list.stream().sorted(Comparator.comparing(PhGoodsInfoForLiveVo::getInventoryUpdateTime));
            } else if (StringUtils.isNotBlank(liveGoodsInfoQuery.getStockUpdateStartTime())) {
                list.stream().sorted(Comparator.comparing(PhGoodsInfoForLiveVo::getStockUpdateTime));
            }
        }
        PagedResult<PhGoodsInfoForLiveVo> pagedResult =
                new PagedResult(list,
                        (int) phGoodsInfoForLiveVoIPage.getCurrent(),
                        phGoodsInfoForLiveVoIPage.getTotal(),
                        (int) phGoodsInfoForLiveVoIPage.getSize());
        pagedResult.setPageSize((int) phGoodsInfoForLiveVoIPage.getSize());
        log.info("查询直播商品数据入参为：{},查询结束时间{}", JSONObject.toJSONString(liveGoodsInfoQuery), LocalDateTime.now());
        return pagedResult;
    }

    @Override
    public InventorBarCodeInfoVo getInventorBarCodeInfo(String inventoryCode) {
        InventorBarCodeInfoVo inventorBarCodeInfo = inventoryMapper.getInventorBarCodeInfo(inventoryCode);
        if (ObjectUtils.isNotEmpty(inventorBarCodeInfo)) {
            if (StringUtils.isBlank(inventorBarCodeInfo.getBarCodeUrl())) {
                inventorBarCodeInfo.setBarCodeUrl(generateBarCode(inventorBarCodeInfo.getInventoryCode(), inventorBarCodeInfo.getInventoryId()));
            }
            if (inventorBarCodeInfo.getInventoryType().equals(YesOrNoEnum.NO.getCode())) {
                inventorBarCodeInfo.setQualityValue("全新");
            }
            return inventorBarCodeInfo;
        }
        throw new BusinessRuntimeException("货品信息获取为空");
    }

    /**
     * 处理数据,增加库存和附件配件字段,属性
     *
     * @param records
     * @return
     */
    private List processData(List<PhGoodsInfoForLiveVo> records) {
        if (CollectionUtils.isEmpty(records)) {
            return null;
        }
        List<Long> inventoryIds = records.stream()
                .map(PhGoodsInfoForLiveVo::getInventoryId).collect(Collectors.toList());
        List<PhInventoryAccessoryVo> inventoryAccessoryByIds =
                phInventoryAccessoryMapper.getInventoryAccessoryByIds(inventoryIds);

        List<PickInventoryStockVo> inventoryStockVoList = inventoryMapper.getInventoryStockInfo(inventoryIds);
        Map<Long, PickInventoryStockVo> stockVoMap = inventoryStockVoList.stream().collect(Collectors.toMap(PickInventoryStockVo::getInventoryId, i -> i));

        records.parallelStream().forEach(item -> {
            item.setAccessoryList(inventoryAccessoryByIds.stream()
                    .filter(ace -> ace.getDictType().equals(DictConstant.INVENTORY_ACCESSORY)
                            && ace.getInventoryId().equals(item.getInventoryId()))
                    .map(PhInventoryAccessoryVo::getDictItemLabel)
                    .collect(Collectors.toList()));// 配件集合
            item.setAnnexList(inventoryAccessoryByIds.stream()
                    .filter(ace -> ace.getDictType().equals(DictConstant.INVENTORY_ANNEX)
                            && ace.getInventoryId().equals(item.getInventoryId()))
                    .map(PhInventoryAccessoryVo::getDictItemLabel)
                    .collect(Collectors.toList()));// 附件集合
            PickInventoryStockVo stockVo = stockVoMap.get(item.getInventoryId());
            if (ObjectUtils.isNotEmpty(stockVo)) {
                item.setWarehousePlace(stockVo.getWarehousePlace());// 仓库位置
                Integer realStock = stockVo.getTotalRealStockNum() == null ? 0 : stockVo.getTotalRealStockNum();
                Integer saleStock = stockVo.getTotalSalesStockNum() == null ? 0 : stockVo.getTotalSalesStockNum();
                item.setTotalStockNum(realStock);// 库存总数
                item.setOccupyStockNum(realStock - saleStock);// 占用数量
                item.setWarehouseCode(stockVo.getWarehouseCode());// 仓库编码
                item.setTotalSalesStockNum(stockVo.getTotalSalesStockNum());// 可售数量
            }
            List<PhPropertyVo> phPropertList = phInventoryPropertyValueMdService
                    .getPropertyValueList(item.getInventoryId());
            if (item.getMaterialId() != null) {
                List<PhPropertyVo> propertyValues = phMaterialPropertyValueMdService
                        .getPropertyValueMap(item.getMaterialId());
                phPropertList.addAll(propertyValues);
            }
            item.setPropertyValueList(phPropertList);// 属性集合
        });
        return records;
    }

    /**
     * 批量生成货品编码
     *
     * @param type
     * @param methodCode
     * @param size
     * @param systemEnum
     * @return
     */
    public List<String> generateInventoryCodes(String type, String methodCode, Integer size, ThirdSystemEnum systemEnum, Integer isSecondhand) {

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyMMdd");
        String format = LocalDate.now().format(formatter);

        // 根据货品编码开头的入库方式获取当天最大自增数
        PhInventoryCodeGenerate generate = phInventoryCodeGenerateService.getLastGenerateByCode(methodCode);

        AtomicInteger autoCode = new AtomicInteger(0);
        if (ObjectUtils.isNotEmpty(generate)) autoCode.set(generate.getAutoCode() + 1);

        Integer digit = redisSupportService.getGenerateDigit();

        List<PhInventoryCodeGenerate> generates = Lists.newArrayList();
        List<String> codeList = Stream.generate(() -> {
            int increment = autoCode.getAndIncrement();
            if (increment >= Math.pow(10, digit))
                throw new BusinessRuntimeException(INVENTORY_CODE_GENERATE_EXCEPTION_800214);

            String biaopin = ObjectUtils.isNotEmpty(isSecondhand) &&
                    isSecondhand.equals(YesOrNoEnum.NO.getCode()) ? "29" : "";
            String inventoryCode = biaopin + methodCode + format
                    + String.format("%0" + digit + "d", increment);

            generates.add(new PhInventoryCodeGenerate()
                    .setType(type).setAutoCode(increment)
                    .setInventoryCode(inventoryCode)
                    .setCreatedDate(LocalDate.now())
                    .setCaller(systemEnum.getCode())
                    .setCode(methodCode));

            return inventoryCode;
        }).limit(size).collect(Collectors.toList());

        if (!phInventoryCodeGenerateService.saveBatch(generates))
            throw new BusinessRuntimeException(INVENTORY_CODE_GENERATE_EXCEPTION_800213);

        return codeList;
    }

    @Override
    public List<PhInventoryEditorVo> getPhInventoryEditName() {
        List<PhInventoryDetail> phInventoryDetails = phInventoryDetailMapper.selectList(
                new LambdaQueryWrapper<PhInventoryDetail>().groupBy(PhInventoryDetail::getFirstEditId));
        return phInventoryDetails.stream().map(item ->
                        new PhInventoryEditorVo()
                                .setFirstEditId(item.getFirstEditId().toString())
                                .setFirstEditName(item.getFirstEditName()))
                .collect(Collectors.toList());
    }

    /**
     * 获取货品条形码图片地址
     * 有则直接返回，没有则生成新的
     *
     * @param inventoryCode 货品编码
     * @return
     */
    @Override
    public String getBarCode(String inventoryCode) {
        Inventory inventory = inventoryService.getInventoryByCode(inventoryCode);
        if (StringUtils.isNotBlank(inventory.getBarCodeUrl())) {
            return inventory.getBarCodeUrl();
        }
        return generateBarCode(inventory.getInventoryCode(), inventory.getId());
    }

    /**
     * 具体生成条形码方法
     *
     * @param inventoryCode
     * @param inventoryId
     * @return
     */
    private String generateBarCode(String inventoryCode, Long inventoryId) {
        try {
            Inventory newInventory = new Inventory();
            String barCodeUrl = BarCodeUtils.generateBarCode(inventoryCode, inventoryCode);
            newInventory.setId(inventoryId);
            newInventory.setBarCodeUrl(barCodeUrl);
            inventoryService.updateById(newInventory);
            return barCodeUrl;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessRuntimeException("条形码生成失败!");
        }
    }

    /**
     * Erp养护增加货品相关成本
     *
     * @return
     */
    @Override
    public Boolean inventoryMaintainCost(InventoryMaintainCostDto costDto) {

        CompletableFuture<PhInventoryGoodsEditDto> oldFuture = CompletableFuture
                .supplyAsync(() -> {
                    PhInventoryGoodsEditDto oldEditDto = new PhInventoryGoodsEditDto();

                    Inventory oldInventory = inventoryService.getInventoryByCode(costDto.getInventoryCode());
                    BeanUtils.copyProperties(oldInventory, oldEditDto);

                    Goods oldGoods = goodsService.getGoodsByInventoryId(oldInventory.getId());
                    BeanUtils.copyProperties(oldGoods, oldEditDto);

                    return oldEditDto.setInventoryId(oldInventory.getId());
                });

        Inventory inventory = inventoryService.getInventoryByCode(costDto.getInventoryCode());
        if (ObjectUtils.isEmpty(inventory))
            throw new BusinessRuntimeException(PARAMETER_IS_INCORRECT_800001);
        Goods goods = goodsService.getGoodsByInventoryId(inventory.getId());
        if (ObjectUtils.isEmpty(goods))
            throw new BusinessRuntimeException(PARAMETER_IS_INCORRECT_800001);

        ((InventoryApiServiceImpl) AopContext.currentProxy()).updateMaintainCost(inventory, goods, costDto);

        PhInventoryGoodsEditDto newEditDto = new PhInventoryGoodsEditDto();
        BeanUtils.copyProperties(inventory, newEditDto);
        BeanUtils.copyProperties(goods, newEditDto);
        newEditDto.setInventoryId(inventory.getId()).setOperatorId(0L).setOperatorName("Erp-养护调用");
        operationRecordLogsCoreService.operationRecordLogs(oldFuture,
                newEditDto, "Erp养护增加货品相关成本记录操作日志异常", newEditDto);
        // 同步会员小程序
        CompletableFuture.runAsync(() -> {
            phinventoryToAmqpService.pushDataToAmqp(
                    Arrays.asList(inventory.getInventoryCode()),
                    SyncAppletTypeEnum.GOODS_PRICE.getCode());
        });
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    public Boolean updateMaintainCost(Inventory inventory, Goods goods, InventoryMaintainCostDto costDto) {
        goods.setSupplyTaxPrice(goods.getMathSupplyTaxPrice().add(costDto.getMaintainCost()));
        if (!goodsService.updateById(goods))
            throw new BusinessRuntimeException(INVENTORY_EDIT_EXCEPTION_800215);

        inventory.setWarehousePrice(inventory.getMathWarehousePrice().add(costDto.getMaintainCost()))
                .setInventoryCost(inventory.getMathInventoryCost().add(costDto.getMaintainCost()))
                .setMaintenancePrice(inventory.getMathMaintenancePrice().add(costDto.getMaintainCost()));
        if (!inventoryService.updateById(inventory))
            throw new BusinessRuntimeException(INVENTORY_EDIT_EXCEPTION_800215);
        return true;
    }

    /**
     * 导入货品编码批量包小检AI识图 - 同步
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<String> aiPictureInventoryBatchSync(List<String> inventoryCodeList) {

        List<Inventory> inventoryList = inventoryService.getInventoryListByCodes(inventoryCodeList);
        if (CollectionUtils.isEmpty(inventoryList))
            throw new BusinessRuntimeException(PARAMETER_IS_INCORRECT_800001);

        CopyOnWriteArrayList<String> copyOnWriteArrayList = new CopyOnWriteArrayList<>();
        inventoryList = inventoryList.stream().map(inventory -> {
            String aiPictureStr = baoxjUtils.aiPictureClassify(inventory.getMainImage());
            if (StringUtils.isEmpty(aiPictureStr)) {
                copyOnWriteArrayList.add(inventory.getInventoryCode());
                return null;
            }

            JSONObject jsonObject = JSON.parseObject(aiPictureStr);
            if (jsonObject.getString("error_msg").equals("Success") && jsonObject.getBoolean("has_match")) {
                return inventory.setItemId(jsonObject.getString("item_ID")).setBxjAiJson(aiPictureStr);
            } else {
                copyOnWriteArrayList.add(inventory.getInventoryCode());
                return null;
            }
        }).filter(ai -> ObjectUtils.isNotEmpty(ai)).collect(Collectors.toList());

        if (CollectionUtils.isNotEmpty(inventoryList))
            inventoryService.updateBatchById(inventoryList);

        return copyOnWriteArrayList;
    }

    /**
     * 导入货品编码批量包小检AI识图 - 异步
     */
    @Override
    public Boolean aiPictureInventoryBatchAsync(List<String> inventoryCodeList) {
        InventoryApiService inventoryApiService = (InventoryApiService) AopContext.currentProxy();
        CompletableFuture.runAsync(() -> {
            ByteArrayOutputStream bos = new ByteArrayOutputStream(); //字节流
            try {
                List<String> failList = inventoryApiService.aiPictureInventoryBatchSync(inventoryCodeList);
                String collect = failList.parallelStream().collect(Collectors.joining("\n"));
                String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("YYYYMMddHHmmss"));
                String fileName = "导入货品编码批量包小检AI识图-识别失败" + time + ".txt";
                bos.write(collect.getBytes(), 0, collect.getBytes().length);
                String failListUrl = QiNiuUploadUtils.uploadExcelFile(bos, fileName);
                log.error("导入货品编码批量包小检AI识图-识别失败 ===>> {}", failListUrl);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("导入货品编码批量包小检AI识图-程序异常 ===>> 入参={}", inventoryCodeList);
            } finally {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        return true;
    }

    @Override
    public String getAllInventorys() {
        log.info("导出货品信息的Excel开始时间为{}", LocalDateTime.now());
        List<PhInventoryExportExcelVo> list = inventoryMapper.getAllInventorys();
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        String url = null;
        try {
            String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("YYYYMMddHHmmss"));
            String fileName = "导出货品信息" + time + ".xlsx";
            String sheetName = "导出货品信息" + time;
            ByteArrayOutputStream bos = new ByteArrayOutputStream();//字节流
            EasyExcel.write(bos, PhInventoryExportExcelVo.class).sheet(sheetName).doWrite(list);
            //调用七牛云的上传方法,上传成功,七牛云会将地址返回
            url = QiNiuUploadUtils.uploadExcelFile(bos, fileName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        log.info("导出货品信息的Excel结束时间为{}", LocalDateTime.now());
        return url;
    }

    /**
     * 批量改价：上传改价文件
     *
     * @return
     */
    @Override
    public InventoryBatchEditResVo batchEditPriceUpload(List<InventoryBatchEditPriceVo> editPriceVoList, OperatorDto operatorDto) {

        // 上传批量改价文件到七牛云
        List<InventoryBatchEditPriceVo> finalEditPriceVoList = editPriceVoList;
        CompletableFuture<String> allListFuture = CompletableFuture.supplyAsync(() -> {
            if (CollectionUtils.isNotEmpty(finalEditPriceVoList)) {
                ByteArrayOutputStream bos = new ByteArrayOutputStream(); //字节流
                try {
                    String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("YYYYMMddHHmmss"));
                    String fileName = "货品批量改价上传文件" + time + ".xlsx";
                    EasyExcel.write(bos, InventoryBatchEditPriceVo.class).sheet("货品价格编辑列表").doWrite(finalEditPriceVoList);
                    return QiNiuUploadUtils.uploadExcelFile(bos, fileName);
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                } finally {
                    try {
                        bos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            } else return null;
        });

        CopyOnWriteArrayList<InventoryBatchEditPriceFailVo> failVoList = new CopyOnWriteArrayList();

        // 参数校验过来批量改价货品列表
        editPriceVoList = editPriceVoList.parallelStream().filter(editPriceVo -> {
            Boolean filter = true;
            StringBuilder failReason = new StringBuilder();
            if (StringUtils.isEmpty(editPriceVo.getInventoryCode())) {
                failReason.append("货品编码不可为空；");
                filter = false;
            }
            BigDecimal warehousePrice = editPriceVo.getWarehousePrice();
            BigDecimal supplyTaxPrice = editPriceVo.getSupplyTaxPrice();
            BigDecimal saleTaxPrice = editPriceVo.getSaleTaxPrice();
            if (ObjectUtils.isEmpty(warehousePrice) && ObjectUtils.isEmpty(supplyTaxPrice) && ObjectUtils.isEmpty(saleTaxPrice)) {
                failReason.append("入库价、含税供货价、含税销售价不可同时为空；");
                filter = false;
            }
            warehousePrice = ObjectUtils.defaultIfNull(warehousePrice, new BigDecimal(0));
            supplyTaxPrice = ObjectUtils.defaultIfNull(supplyTaxPrice, warehousePrice);
            saleTaxPrice = ObjectUtils.defaultIfNull(saleTaxPrice, supplyTaxPrice);
            if (!(warehousePrice.compareTo(supplyTaxPrice) <= 0 && supplyTaxPrice.compareTo(saleTaxPrice) <= 0)) {
                failReason.append("正确的价格关系应该是：入库价 <= 含税供货价 <= 含税销售价；");
                filter = false;
            }
            if (BooleanUtils.isNotTrue(filter)) {
                InventoryBatchEditPriceFailVo priceFailVo = ProBeanUtils
                        .copyProperties(editPriceVo, InventoryBatchEditPriceFailVo.class);
                priceFailVo.setFailReason(failReason.toString());
                failVoList.add(priceFailVo);
            }
            return filter;
        }).collect(Collectors.toList());

        // 查询相关货品库存信息
        List<String> ivtCodes = editPriceVoList.parallelStream()
                .map(InventoryBatchEditPriceVo::getInventoryCode).collect(Collectors.toList());
        ReturnResponseBean<List<InventoryStockWhVO>> stockResponseBean =
                stockCommonService.getReallyStockInfo(ivtCodes);
        if (stockResponseBean.isError())
            throw new BusinessRuntimeException(INVENTORY_BATCH_EDIT_EXCEPTION_800220);

        Map<String, Integer> stockMap = stockResponseBean.getData().parallelStream()
                .collect(Collectors.toMap(InventoryStockWhVO::getInventoryCode, stockVo -> {
                    Integer freeStockNum = stockVo.getFreeStockNum();
                    if (ObjectUtils.isEmpty(freeStockNum)) return null;
                    Integer reduce = stockVo.getWhVos().parallelStream()
                            .map(StockWarehouseVO::getRealStockNum).reduce(0, Integer::sum);
                    return reduce + freeStockNum;
                }));

        // 库存校验过来批量改价货品列表
        editPriceVoList = editPriceVoList.parallelStream().filter(editPriceVo -> {
            InventoryBatchEditPriceFailVo priceFailVo = ProBeanUtils
                    .copyProperties(editPriceVo, InventoryBatchEditPriceFailVo.class);
            Integer stockNum = stockMap.get(editPriceVo.getInventoryCode());
            if (ObjectUtils.isEmpty(stockNum)) {
                priceFailVo.setFailReason("货品编码错误未查询到对应货品库存信息；");
                failVoList.add(priceFailVo);
                return false;
            }
            if (stockNum == 0) {
                priceFailVo.setFailReason("库存数为0的货品不可修改价格；");
                failVoList.add(priceFailVo);
                return false;
            }
            return true;
        }).collect(Collectors.toList());

        // 重复货品编码的以最后一条记录的修改价格为准
        editPriceVoList = editPriceVoList.parallelStream().collect(Collectors
                        .groupingBy(InventoryBatchEditPriceVo::getInventoryCode, Collectors.toList()))
                .entrySet().parallelStream().map(entry -> entry.getValue().parallelStream()
                        .sorted((vo1, vo2) -> vo2.getIndex() - vo1.getIndex()).findFirst().get()).collect(Collectors.toList());

        List<PhInventoryGoodsEditDto> oldEditList = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(editPriceVoList)) {
            // 货品批量改价单独记录日志 - 查询原值
            List<String> inventoryCodes = editPriceVoList.parallelStream()
                    .map(InventoryBatchEditPriceVo::getInventoryCode).collect(Collectors.toList());
            oldEditList = goodsMapper.getGoodsEditDtoByInventoryCodes(inventoryCodes);

            Map<String, PhInventoryGoodsEditDto> costMap = oldEditList.parallelStream().collect(Collectors
                    .toMap(PhInventoryGoodsEditDto::getInventoryCode, Function.identity()));

            List<PhInventoryGoodsEditDto> finalOldEditList1 = oldEditList;
            editPriceVoList = editPriceVoList.parallelStream().filter(editPriceVo -> {
                PhInventoryGoodsEditDto oldEdit = costMap.get(editPriceVo.getInventoryCode());
                if (ObjectUtils.isEmpty(oldEdit)) {
                    InventoryBatchEditPriceFailVo priceFailVo = ProBeanUtils
                            .copyProperties(editPriceVo, InventoryBatchEditPriceFailVo.class);
                    priceFailVo.setFailReason("货品编码错误未查询到对应货品信息");
                    failVoList.add(priceFailVo);
                    finalOldEditList1.remove(oldEdit);
                    return false;
                }
                BigDecimal warehousePrice = ObjectUtils.defaultIfNull(editPriceVo.getWarehousePrice(), new BigDecimal(Integer.MAX_VALUE));
                BigDecimal supplyTaxPrice = ObjectUtils.defaultIfNull(editPriceVo.getSupplyTaxPrice(), warehousePrice);
                BigDecimal saleTaxPrice = ObjectUtils.defaultIfNull(editPriceVo.getSaleTaxPrice(), supplyTaxPrice);
                if (warehousePrice.compareTo(oldEdit.getInventoryCost()) < 0 ||
                        supplyTaxPrice.compareTo(oldEdit.getInventoryCost()) < 0 ||
                        saleTaxPrice.compareTo(oldEdit.getInventoryCost()) < 0) {
                    InventoryBatchEditPriceFailVo priceFailVo = ProBeanUtils
                            .copyProperties(editPriceVo, InventoryBatchEditPriceFailVo.class);
                    priceFailVo.setFailReason("正确的价格关系应该是：成本价(" + oldEdit.getInventoryCost() + ") <= 入库价 <= 含税供货价 <= 含税销售价");
                    failVoList.add(priceFailVo);
                    finalOldEditList1.remove(oldEdit);
                    return false;
                }
                return true;
            }).collect(Collectors.toList());
            oldEditList = finalOldEditList1;

            // 货品和商品相关价格数据库修改
            ((InventoryApiServiceImpl) AopContext.currentProxy()).batchEditPriceTransactional(editPriceVoList, oldEditList);

            // 批量改价后同步胖总管
            CompletableFuture.runAsync(() ->
                    inventoryService.getInventoryListByCodes(inventoryCodes).stream().forEach(inventory ->
                            syncThirdParties(inventory.getId(), operatorDto.getOperatorName())));
            // 批量改价后同步会员小程序
            CompletableFuture.runAsync(() -> phinventoryToAmqpService
                    .pushDataToAmqp(inventoryCodes, SyncAppletTypeEnum.GOODS_PRICE.getCode()));
        }

        // 货品批量改价单独记录日志 - 处理新值并对比原值和新值
        Map<String, InventoryBatchEditPriceVo> editPriceVoMap = editPriceVoList.parallelStream()
                .collect(Collectors.toMap(InventoryBatchEditPriceVo::getInventoryCode, Function.identity()));
        List<PhInventoryGoodsEditDto> finalOldEditList2 = oldEditList;
        CompletableFuture.runAsync(() -> {
            // 遍历每一个货品商品单独记录编辑日志
            finalOldEditList2.parallelStream().forEach(oldEditDto -> {
                // 处理参数
                CompletableFuture<PhInventoryGoodsEditDto> oldEditDtoFuture = CompletableFuture.completedFuture(oldEditDto);
                InventoryBatchEditPriceVo editPriceVo = editPriceVoMap.get(oldEditDto.getInventoryCode());
                PhInventoryGoodsEditDto newEditDto = ProBeanUtils
                        .copyProperties(editPriceVo, PhInventoryGoodsEditDto.class)
                        .setInventoryId(oldEditDto.getInventoryId())
                        .setGoodsId(oldEditDto.getGoodsId())
                        .setInventoryCost(oldEditDto.getInventoryCost());
                // 操作数据库记录日志
                operationRecordLogsCoreService.operationRecordLogs(oldEditDtoFuture,
                        newEditDto, "货品批量改价单独记录日志异常", operatorDto);
            });

            log.info("InventoryApiService#batchEditPriceUpload ===>> 货品批量改价单独记录日志全部完成");
        }).exceptionally(e -> {
            e.printStackTrace();
            log.error("InventoryApiService#batchEditPriceUpload ===>> 货品批量改价单独记录日志线程程序异常 ");
            return null;
        });

        // 上传改价失败文件到七牛云
        CompletableFuture<String> failListFuture = CompletableFuture.supplyAsync(() -> {
            if (CollectionUtils.isNotEmpty(failVoList)) {
                ByteArrayOutputStream bos = new ByteArrayOutputStream(); //字节流
                try {
                    String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("YYYYMMddHHmmss"));
                    String fileName = "货品批量改价失败信息列表" + time + ".xlsx";
                    EasyExcel.write(bos, InventoryBatchEditPriceFailVo.class).sheet("失败信息").doWrite(failVoList);
                    //调用七牛云的上传方法,上传成功,七牛云会将地址返回
                    return QiNiuUploadUtils.uploadExcelFile(bos, fileName);
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                } finally {
                    try {
                        bos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            } else return null;
        });

        // 货品批量改价记录日志 并将最后结果返回
        return CompletableFuture.allOf(allListFuture, failListFuture).thenApply(x -> {

            // 货品批量改价记录日志
            CompletableFuture.runAsync(() -> {
                PhInventoryBatchEditPriceLogs editPriceLogs = new PhInventoryBatchEditPriceLogs()
                        .setFailNum(failVoList.size())
                        .setUploadUrl(allListFuture.join())
                        .setDownloadUrl(failListFuture.join())
                        .setOperatorId(operatorDto.getOperatorId())
                        .setOperatorName(operatorDto.getOperatorName())
                        .setSuccessNum(finalEditPriceVoList.size() - failVoList.size());
                if (!phInventoryBatchEditPriceLogsService.save(editPriceLogs))
                    log.error("InventoryApiService#batchEditPriceUpload ===>> 货品批量改价记录日志异常 " +
                            "===>> 入参={}", JSON.toJSONString(editPriceLogs));
            });

            return new InventoryBatchEditResVo()
                    .setSuccessNum(finalEditPriceVoList.size() - failVoList.size())
                    .setFailNum(failVoList.size())
                    .setFailUrl(failListFuture.join());
        }).join();
    }

    /**
     * 批量修改货品商品相关价格数据
     *
     * @param newEditList
     */
    @Transactional(rollbackFor = Exception.class)
    public void batchEditPriceTransactional(List<InventoryBatchEditPriceVo> newEditList, List<PhInventoryGoodsEditDto> oldEditList) {
        if (CollectionUtils.isNotEmpty(newEditList)) {
            try {
//                goodsMapper.batchUpdateGoodsPrices(editPriceVoList);

                Map<String, InventoryBatchEditPriceVo> editMap = newEditList.parallelStream()
                        .collect(Collectors.toMap(InventoryBatchEditPriceVo::getInventoryCode, Function.identity()));

                List<Inventory> inventoryList = oldEditList.parallelStream().map(oldEditDto ->
                                new Inventory().setId(oldEditDto.getInventoryId())
                                        .setWarehousePrice(editMap.get(oldEditDto.getInventoryCode()).getWarehousePrice()))
                        .filter(inventory -> ObjectUtils.isNotEmpty(inventory.getWarehousePrice()))
                        .collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(inventoryList)) inventoryService.updateBatchById(inventoryList);

                List<Goods> goodsList = oldEditList.parallelStream().map(oldEditDto ->
                                new Goods().setId(oldEditDto.getGoodsId())
                                        .setSupplyTaxPrice(editMap.get(oldEditDto.getInventoryCode()).getSupplyTaxPrice())
                                        .setSaleTaxPrice(editMap.get(oldEditDto.getInventoryCode()).getSaleTaxPrice()))
                        .filter(goods -> ObjectUtils.isNotEmpty(goods.getSupplyTaxPrice()) || ObjectUtils.isNotEmpty(goods.getSaleTaxPrice()))
                        .collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(goodsList)) goodsService.updateBatchById(goodsList);
            } catch (Exception e) {
                e.printStackTrace();
                throw new BusinessRuntimeException(INVENTORY_BATCH_EDIT_EXCEPTION_800221);
            }
        }
    }

    /**
     * 批量修改备注：上传修改备注文件
     *
     * @return
     */
    @Override
    public InventoryBatchEditResVo batchEditRemarksUpload(List<InventoryBatchEditRemarksVo> editRemarksVoList, int type, OperatorDto operatorDto) {

        // 上传批量修改备注文件到七牛云
        List<InventoryBatchEditRemarksVo> finalEditRemarksVoList = editRemarksVoList;
        CompletableFuture<String> allListFuture = CompletableFuture.supplyAsync(() -> {
            if (CollectionUtils.isNotEmpty(finalEditRemarksVoList)) {
                ByteArrayOutputStream bos = new ByteArrayOutputStream(); //字节流
                try {
                    String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("YYYYMMddHHmmss"));
                    String fileName = "货品批量修改备注上传文件" + time + ".xlsx";
                    EasyExcel.write(bos, InventoryBatchEditRemarksVo.class).sheet("货品备注编辑列表").doWrite(finalEditRemarksVoList);
                    return QiNiuUploadUtils.uploadExcelFile(bos, fileName);
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                } finally {
                    try {
                        bos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            } else return null;
        });

        CopyOnWriteArrayList<InventoryBatchEditRemarksFailVo> failVoList = new CopyOnWriteArrayList();

        // 参数校验过来批量修改备注货品列表
        editRemarksVoList = editRemarksVoList.parallelStream().filter(editRemarksVo -> {
            Boolean filter = true;
            StringBuilder failReason = new StringBuilder();
            if (StringUtils.isEmpty(editRemarksVo.getInventoryCode())) {
                failReason.append("货品编码不可为空；");
                filter = false;
            }
            if (type != 3 && StringUtils.isEmpty(editRemarksVo.getRemarks())) {
                failReason.append("货品备注不可为空；");
                filter = false;
            }
            if (BooleanUtils.isNotTrue(filter)) {
                InventoryBatchEditRemarksFailVo failVo = ProBeanUtils
                        .copyProperties(editRemarksVo, InventoryBatchEditRemarksFailVo.class);
                failVo.setResult("失败");
                failVo.setFailReason(failReason.toString());
                failVoList.add(failVo);
            }
            return filter;
        }).collect(Collectors.toList());

        // 查询相关货品库存信息
        List<String> ivtCodes = editRemarksVoList.parallelStream()
                .map(InventoryBatchEditRemarksVo::getInventoryCode).collect(Collectors.toList());
        ReturnResponseBean<List<InventoryStockWhVO>> stockResponseBean =
                stockCommonService.getReallyStockInfo(ivtCodes);
        if (stockResponseBean.isError())
            throw new BusinessRuntimeException(INVENTORY_BATCH_EDIT_EXCEPTION_800220);

        Map<String, Integer> stockMap = stockResponseBean.getData().parallelStream()
                .collect(Collectors.toMap(InventoryStockWhVO::getInventoryCode, stockVo -> {
                    Integer freeStockNum = stockVo.getFreeStockNum();
                    if (ObjectUtils.isEmpty(freeStockNum)) return null;
                    Integer reduce = stockVo.getWhVos().parallelStream()
                            .map(StockWarehouseVO::getRealStockNum).reduce(0, Integer::sum);
                    return reduce + freeStockNum;
                }));

        // 库存校验过来批量修改备注货品列表
        editRemarksVoList = editRemarksVoList.parallelStream().filter(editPriceVo -> {
            InventoryBatchEditRemarksFailVo failVo = ProBeanUtils
                    .copyProperties(editPriceVo, InventoryBatchEditRemarksFailVo.class);
            Integer stockNum = stockMap.get(editPriceVo.getInventoryCode());
            if (ObjectUtils.isEmpty(stockNum)) {
                failVo.setResult("失败");
                failVo.setFailReason("货品编码错误未查询到对应货品库存信息；");
                failVoList.add(failVo);
                return false;
            }
            if (stockNum == 0) {
                failVo.setResult("失败");
                failVo.setFailReason("库存数为0的货品不可修改备注；");
                failVoList.add(failVo);
                return false;
            }
            return true;
        }).collect(Collectors.toList());

        // 重复货品编码的以最后一条记录的修改备注为准
        editRemarksVoList = editRemarksVoList.parallelStream().collect(Collectors
                        .groupingBy(InventoryBatchEditRemarksVo::getInventoryCode, Collectors.toList()))
                .entrySet().parallelStream().map(entry -> entry.getValue().parallelStream()
                        .sorted((vo1, vo2) -> vo2.getIndex() - vo1.getIndex()).findFirst().get()).collect(Collectors.toList());

        List<PhInventoryGoodsEditDto> oldEditList = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(editRemarksVoList)) {

            // 货品批量修改备注单独记录日志 - 查询原值
            List<String> inventoryCodes = editRemarksVoList.parallelStream()
                    .map(InventoryBatchEditRemarksVo::getInventoryCode).collect(Collectors.toList());
            List<Inventory> inventoryList = inventoryService.getInventoryListByCodes(inventoryCodes);
            oldEditList.addAll(inventoryList.stream().map(inventory -> ProBeanUtils
                            .copyProperties(inventory, PhInventoryGoodsEditDto.class).setInventoryId(inventory.getId()))
                    .collect(Collectors.toList()));

            Map<String, String> remarksMap = inventoryList.stream().collect(Collectors
                    .toMap(Inventory::getInventoryCode, inventory -> ObjectUtils.defaultIfNull(inventory.getRemarks(), "")));
            editRemarksVoList = editRemarksVoList.stream().map(editRemarksVo -> {
                switch (type) {
                    case 1: // 追加
                        String newRemarks = remarksMap.get(editRemarksVo.getInventoryCode()) + " " + editRemarksVo.getRemarks();
                        editRemarksVo.setRemarks(newRemarks.trim());
                        break;
                    case 2: // 覆盖
                        break;
                    case 3: // 删除
                        editRemarksVo.setRemarks("");
                        break;
                }
                return editRemarksVo;
            }).collect(Collectors.toList());

            // 货品和商品相关价格数据库修改
            ((InventoryApiServiceImpl) AopContext.currentProxy()).batchEditRemarksTransactional(editRemarksVoList, oldEditList);

            // 批量修改备注后同步胖总管
            CompletableFuture.runAsync(() ->
                    inventoryService.getInventoryListByCodes(inventoryCodes).stream().forEach(inventory ->
                            syncThirdParties(inventory.getId(), operatorDto.getOperatorName())));
            // 批量修改备注后同步会员小程序
            CompletableFuture.runAsync(() -> phinventoryToAmqpService
                    .pushDataToAmqp(inventoryCodes, SyncAppletTypeEnum.PH_INVENTORY_DATA.getCode()));
        }

        // 货品批量修改备注单独记录日志 - 处理新值并对比原值和新值
        Map<String, String> editRemarksVoMap = editRemarksVoList.parallelStream().collect(Collectors
                .toMap(InventoryBatchEditRemarksVo::getInventoryCode, InventoryBatchEditRemarksVo::getRemarks));
        CompletableFuture.runAsync(() -> {
            // 遍历每一个货品商品单独记录编辑日志
            oldEditList.stream().forEach(oldEditDto -> {
                // 处理参数
                PhInventoryGoodsEditDto newEditDto = ProBeanUtils
                        .copyProperties(oldEditDto, PhInventoryGoodsEditDto.class)
                        .setRemarks(editRemarksVoMap.get(oldEditDto.getInventoryCode()));
                // 操作数据库记录日志
                operationRecordLogsCoreService.operationRecordLogs(CompletableFuture.completedFuture(oldEditDto),
                        newEditDto, "货品批量修改备注单独记录日志异常", operatorDto);
            });

            log.info("InventoryApiService#batchEditRemarksUpload ===>> 货品批量修改备注单独记录日志全部完成");
        }).exceptionally(e -> {
            e.printStackTrace();
            log.error("InventoryApiService#batchEditRemarksUpload ===>> 货品批量修改备注单独记录日志线程程序异常 ");
            return null;
        });

        // 上传改价失败文件到七牛云
        CompletableFuture<String> failListFuture = CompletableFuture.supplyAsync(() -> {
            if (CollectionUtils.isNotEmpty(failVoList)) {
                ByteArrayOutputStream bos = new ByteArrayOutputStream(); //字节流
                try {
                    String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("YYYYMMddHHmmss"));
                    String fileName = "货品批量修改备注失败信息列表" + time + ".xlsx";
                    EasyExcel.write(bos, InventoryBatchEditRemarksFailVo.class).sheet("失败信息").doWrite(failVoList);
                    //调用七牛云的上传方法,上传成功,七牛云会将地址返回
                    return QiNiuUploadUtils.uploadExcelFile(bos, fileName);
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                } finally {
                    try {
                        bos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            } else return null;
        });

        // 货品批量修改备注记录日志 并将最后结果返回
        return CompletableFuture.allOf(allListFuture, failListFuture).thenApply(x -> {

            // 货品批量修改备注记录日志
            CompletableFuture.runAsync(() -> {
                PhInventoryBatchEditPriceLogs editRemarksLogs = new PhInventoryBatchEditPriceLogs()
                        .setFailNum(failVoList.size())
                        .setUploadUrl(allListFuture.join())
                        .setDownloadUrl(failListFuture.join())
                        .setOperatorId(operatorDto.getOperatorId())
                        .setOperatorName(operatorDto.getOperatorName())
                        .setSuccessNum(finalEditRemarksVoList.size() - failVoList.size());
                if (!phInventoryBatchEditPriceLogsService.save(editRemarksLogs))
                    log.error("InventoryApiService#batchEditRemarksUpload ===>> 货品批量修改备注记录日志异常 " +
                            "===>> 入参={}", JSON.toJSONString(editRemarksLogs));
            });

            return new InventoryBatchEditResVo()
                    .setSuccessNum(finalEditRemarksVoList.size() - failVoList.size())
                    .setFailNum(failVoList.size())
                    .setFailUrl(failListFuture.join());
        }).join();
    }

    /**
     * 批量修改货品商品相关价格数据
     *
     * @param newEditList
     */
    @Transactional(rollbackFor = Exception.class)
    public void batchEditRemarksTransactional(List<InventoryBatchEditRemarksVo> newEditList, List<PhInventoryGoodsEditDto> oldEditList) {
        if (CollectionUtils.isNotEmpty(newEditList)) {
            try {
                Map<String, String> editMap = newEditList.parallelStream().collect(Collectors
                        .toMap(InventoryBatchEditRemarksVo::getInventoryCode, InventoryBatchEditRemarksVo::getRemarks));

                List<Inventory> inventoryList = oldEditList.parallelStream().map(oldEditDto ->
                                new Inventory().setId(oldEditDto.getInventoryId())
                                        .setRemarks(editMap.get(oldEditDto.getInventoryCode())))
                        .collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(inventoryList)) inventoryService.updateBatchById(inventoryList);
            } catch (Exception e) {
                e.printStackTrace();
                throw new BusinessRuntimeException(INVENTORY_BATCH_EDIT_EXCEPTION_800221);
            }
        }
    }

    /**
     * 下载工具：新建下载任务
     *
     * @param inventoryCodeList
     * @param operatorDto
     * @return
     */
    @Override
    public List<InventoryDownLoadFailVo> createDownloadTask(List<String> inventoryCodeList, OperatorDto operatorDto) {
        String traceId = traceIdLocal.get();
        log.info("createDownloadTask ===>> [{}] ===>> 开始执行 ===>> {} ===>> {}",
                traceId, JSON.toJSONString(operatorDto), JSON.toJSONString(inventoryCodeList));

        CompletableFuture<String> uploadFuture = CompletableFuture.supplyAsync(() -> {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            try {
                String collect = inventoryCodeList.parallelStream().collect(Collectors.joining("\n"));
                String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("YYYYMMddHHmmss"));
                String nextInt = String.format("%0" + 5 + "d", new Random().nextInt(100000));
                String fileName = "新建下载任务货品编码" + time + "-" + nextInt + ".txt";
                bos.write(collect.getBytes(), 0, collect.getBytes().length);
                String uploadUrl = QiNiuUploadUtils.uploadExcelFile(bos, fileName);
                log.info("createDownloadTask ===>> [{}] ===>> 上传新建下载任务货品编码 ===>> 成功 ===>> {}", traceId, uploadUrl);
                return uploadUrl;
            } catch (Exception e) {
                e.printStackTrace();
                log.error("createDownloadTask ===>> [{}] ===>> 上传新建下载任务货品编码 ===>> 异常", traceId);
                return null;
            } finally {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

        Map<String, Inventory> ivtMap = inventoryService.getInventoryListByCodes(inventoryCodeList)
                .parallelStream().collect(Collectors.toMap(Inventory::getInventoryCode, Function.identity()));

        ConcurrentHashMap<String, List<String>> imgMap = new ConcurrentHashMap();

        List<InventoryDownLoadFailVo> failVoList = inventoryCodeList.parallelStream().map(inventoryCode -> {
            Inventory inventory = ivtMap.get(inventoryCode);
            if (ObjectUtils.isEmpty(inventory))
                return new InventoryDownLoadFailVo()
                        .setInventoryCode(inventoryCode).setMsg("货品不存在");
            String detailImage = inventory.getDetailImage();
            if (StringUtils.isEmpty(detailImage))
                return new InventoryDownLoadFailVo()
                        .setInventoryCode(inventoryCode).setMsg("货品未上传图片");
            imgMap.put(inventoryCode, Arrays.asList(detailImage.split("\\|")));
            return null;
        }).filter(ObjectUtils::isNotEmpty).collect(Collectors.toList());

        if (imgMap.size() > 0) { // 开启任务
            PhInventoryDownloadLogs downloadLogs = new PhInventoryDownloadLogs()
                    .setUploadUrl(uploadFuture.join())
                    .setOperatorId(operatorDto.getOperatorId())
                    .setOperatorName(operatorDto.getOperatorName())
                    .setStatus(CommonConstant.STATUS_IN_PROGRESS);
            if (!phInventoryDownloadLogsService.save(downloadLogs))
                throw new BusinessRuntimeException(INVENTORY_DOWNLOAD_TOOLS_EXCEPTION_800224);
            log.info("createDownloadTask ===>> [{}] ===>> 保存新建任务操作日志成功 ===>> {}", traceId, JSON.toJSONString(downloadLogs));

            String separator = System.getProperty("file.separator");
            CompletableFuture.runAsync(() -> {

                String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("YYYYMMddHHmmss"));
                String nextInt = String.format("%0" + 5 + "d", new Random().nextInt(100000));
                String curTempDir = tempDir + time + "-" + nextInt;
                String curTempZip = tempZip + "新建下载任务货品图片集合" + time + "-" + nextInt + ".zip";
                ExecutorService executorService = Executors.newFixedThreadPool(Math.min(inventoryCodeList.size(), 100));
                String uploadExcelFile = null;
                try {
                    log.info("createDownloadTask ===>> [{}] ===>> 开始下载货品图片 ===>> 货品数量={}", traceId, imgMap.size());
                    AtomicInteger failImgAuto = new AtomicInteger(0);
                    AtomicInteger successImgAuto = new AtomicInteger(0);
                    CompletableFuture[] ivtFutureArr = new CompletableFuture[imgMap.size()];
                    int cfIndex = 0;
                    for (Map.Entry<String, List<String>> entry : imgMap.entrySet()) {
                        ivtFutureArr[cfIndex++] = CompletableFuture.runAsync(() -> {
                            String inventoryCode = entry.getKey();
                            File dir = new File(String.format("%s%s%s", curTempDir, separator, inventoryCode));
                            if (!dir.exists()) dir.mkdirs();
                            AtomicInteger auto = new AtomicInteger(0);

                            entry.getValue().stream().forEach(imgUrl -> {
                                InputStream inputStream = null;
                                try {
                                    String ext = imgUrl.toLowerCase().indexOf(".jpg") != -1 ? "jpg" : "png";
                                    inputStream = CommonUtils.getInputStreamByUrl(imgUrl);
                                    Files.copy(inputStream, Paths.get(String.format("%s%s%s%s%s.%s", curTempDir,
                                            separator, inventoryCode, separator, auto.incrementAndGet(), ext)));
                                    successImgAuto.incrementAndGet();
                                } catch (Exception e) {
                                    e.printStackTrace();
                                    log.error("createDownloadTask ===>> [{}] ===>> 创建货品图片异常-{} ===>> {} ===>> {}",
                                            traceId, failImgAuto.incrementAndGet(), inventoryCode, imgUrl);
                                } finally {
                                    if (inputStream != null) {
                                        try {
                                            inputStream.close();
                                        } catch (IOException e) {
                                            e.printStackTrace();
                                        }
                                    }
                                }
                            });
                        }, executorService);
                    }
                    CompletableFuture.allOf(ivtFutureArr).join();
                    log.info("createDownloadTask ===>> [{}] ===>> 下载货品图片 ===>> 成功数量={} ===>> 失败数量={}",
                            traceId, successImgAuto.get(), failImgAuto.get());

                    log.info("createDownloadTask ===>> [{}] ===>>  [{}] ===>> 开始压缩 ===>> {} ===>> {}",
                            traceId, downloadLogs.getId(), curTempDir, curTempZip);
                    CommonUtils.zipFileMap(curTempDir, curTempZip);
                    log.info("createDownloadTask ===>> [{}] ===>>  [{}] ===>> 完成压缩 ===>> {} ===>> {}",
                            traceId, downloadLogs.getId(), curTempDir, curTempZip);

                    uploadExcelFile = QiNiuUploadUtils.uploadFile(new File(curTempZip), FileTypeEnum.FILE);
                    log.info("createDownloadTask ===>> [{}] ===>> [{}] ===>> 上传下载结果到七牛云成功 ===>> {}",
                            traceId, downloadLogs.getId(), uploadExcelFile);

                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("createDownloadTask ===>> [{}] ===>> [{}] ===>> 执行下载任务程序异常 ===>> Error={}",
                            traceId, downloadLogs.getId(), e.getMessage());
                } finally {
                    if (!executorService.isShutdown()) executorService.shutdown();

                    CommonUtils.delFileOrDirectory(curTempZip);
                    CommonUtils.delFileOrDirectory(curTempDir);
                    log.info("createDownloadTask ===>> [{}] ===>> [{}] ===>> 删除临时文件完成", traceId, downloadLogs.getId());

                    PhInventoryDownloadLogs updLogs = new PhInventoryDownloadLogs()
                            .setId(downloadLogs.getId())
                            .setDownloadUrl(uploadExcelFile)
                            .setStatus(StringUtils.isEmpty(uploadExcelFile) ?
                                    CommonConstant.STATUS_FAIL : CommonConstant.STATUS_COMPLETED);
                    boolean updateById = phInventoryDownloadLogsService.updateById(updLogs);
                    log.info("createDownloadTask ===>> [{}] ===>> [{}] ===>> 修改新建任务操作日志状态 ===>> {} ===>> {}",
                            traceId, downloadLogs.getId(), updateById ? "成功" : "异常", JSON.toJSONString(updLogs));
                }
            });
        }

        return failVoList;
    }

    /**
     * 下载工具：查询任务列表
     *
     * @return
     */
    @Override
    public PagedResult<InventoryDownLoadListVo> getDownloadTaskList(InventoryDownloadTaskQuery taskQuery) {

        IPage<PhInventoryDownloadLogs> iPage = phInventoryDownloadLogsService.getDownloadTaskList(taskQuery);

        List<InventoryDownLoadListVo> logsVos = iPage.getRecords().stream().map(logs ->
                ProBeanUtils.copyProperties(logs, InventoryDownLoadListVo.class)).collect(Collectors.toList());

        // 构建响应数据分页模型
        return new PagedResult<>(logsVos, (int) iPage.getCurrent(), iPage.getTotal(), (int) iPage.getSize());
    }

    @Override
    public List<PhInventoryToAmqpVo> queryGoodsinfoForApplet(List<String> inventoryCodes) {
        // 查询所有库存信息
        Map<String, Integer> stockMap = stockCommonService.getSaleStockMqVoList(inventoryCodes)
                .stream().collect(Collectors.toMap(StockMqVO::getInventoryCode, StockMqVO::getStock));
        return inventoryService.getInventoryBasicByCode(inventoryCodes).stream().map(item -> {
            item.setStockNum(stockMap.get(item.getInventoryCode()));
            return item;
        }).collect(Collectors.toList());
    }

    /**
     * 保存养护结果
     * @param inmaintrsl
     * @return
     */
    @Override
    public Boolean inventoryMaintainResult(InventoryMaintainResultDto inmaintrsl) {
        log.info("更新养护结果开始,入参为:{}",JSON.toJSONString(inmaintrsl));
        Boolean result = false;
        String errorMsg = null;
        try {
            Inventory inventory = inventoryService.getInventoryByCode(inmaintrsl.getInventoryCode());
            if (ObjectUtils.isEmpty(inventory)){
                errorMsg = "货品不存在!";
                return result;
            }


            result = ((InventoryApiServiceImpl) AopContext.currentProxy()).updateMaintainResult(inventory, inmaintrsl);

            // 记录日志
            CompletableFuture.runAsync(() -> {
                String textContent = "检测结果为："+inmaintrsl.getMaintainResult();
                if(StringUtils.isNotBlank(inmaintrsl.getMaintainRemarks())){
                    textContent = textContent + "，备注为："+inmaintrsl.getMaintainRemarks();
                }
                operationRecordLogsCoreService.addTextAddOperationRecordLogs(
                        new LogsForTextAddDto()
                                .setBusinessTable(CommonConstant.BUSINESS_TYPE_INVENTORY)
                                .setRecordId(inventory.getId())
                                .setOperator("ERP-更新养护结果")
                                .setOperatorId(0L)
                                .setTextContent(textContent));
            });
        } catch (Exception e) {
            log.error("更新养护结果失败,货品编码为:{},错误信息为:{}",inmaintrsl.getInventoryCode(),e);
            e.printStackTrace();
            errorMsg = e.getMessage();
        }finally {
            if (BooleanUtils.isFalse(result)){
                // 对接钉钉保障提醒
                String msg = dingTalkHandler.getMsgForMaintainResult(inmaintrsl,errorMsg);
                dingTalkHandler.sendMsg(msg, new ArrayList());
            }
        }
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    public Boolean updateMaintainResult(Inventory inventory, InventoryMaintainResultDto inmaintrsl) {
        inventory.setMaintainResult(inmaintrsl.getMaintainResult())
                .setMaintainRemarks(inmaintrsl.getMaintainRemarks());
        return inventoryService.updateById(inventory);
    }
}
